package edu.cmu.cs.erl;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import jp.ac.kobe_u.cs.prolog.lang.*;

/** Using Builder Pattern to create Prolog terms.*/
public class TermBuilder {
	/**
	 * Create a term for an and connective
	 * Will look like:
	 * lhs and rhs
	 * @param lhs The left side of the and
	 * @param rhs The right side of the and
	 */
	static public Term createAndTerm(Term lhs, Term rhs) {
		return new StructureTerm(SymbolTerm.makeSymbol("and", 2), new Term[]{lhs, rhs});
	}
	
/*	static public Term createAtomicTerm(Term term) {
		return new StructureTerm(SymbolTerm.makeSymbol("at", 1), new Term[]{term});
	}
*/
	
	static public Term createAtomicTerm(Object obj) {
		if (obj instanceof VariableTerm) {
			return new StructureTerm(SymbolTerm.makeSymbol("at", 1), new Term[]{(VariableTerm)obj});
		}
		else
			return new StructureTerm(SymbolTerm.makeSymbol("at", 1), new Term[]{new JavaObjectTerm(obj)});
	}
	
	
	/**
	 * Create an atomic level term
	 * @param name The term name. This can not be the same as one of the pre-defined terms.
	 * @param args The arguments. These must also be atomic level terms or variables.
	 */
/*	static public Term createAtomicTerm(String name, Term[] args) {
		//TODO: check the name and args to make sure they match the preconditions.
		//return new StructureTerm(SymbolTerm.makeSymbol(name, args.length), args);
		return new StructureTerm(SymbolTerm.makeSymbol("at", 2), new Term[]{name, args});
		
	}*/
	
	/**
	 * Create a new variable term. These can be cloned if they are to be used multiple times.
	 */
	@Deprecated
	static public VariableTerm createVariableTerm() {
		VariableTerm tm =  new VariableTerm();
		return tm;
	}

	/**
	 * Create a term for a forall connective, but lazy-loading
	 * Will look like:
	 * forall variable in coll do inner
	 * @param coll An object that we will call back with to get the real list later
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	static public Term createForAllLazyTerm(Oracle oracle, Object coll, String variableName, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("forallLazy", 4);
		VariableTerm variable = oracle.getVariable(variableName);
		return new StructureTerm(name, new Term[]{variable, new JavaObjectTerm(variableName), new JavaObjectTerm(coll), inner});
	}

	/**
	 * Create a term for a forall connective, but lazy-loading
	 * Will look like:
	 * forall variable in coll do inner
	 * @param coll An object that we will call back with to get the real list later
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	@Deprecated
	static public Term createForAllLazyTerm(Object coll, String variableName, VariableTerm variable, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("forallLazy", 4);
		return new StructureTerm(name, new Term[]{variable, new JavaObjectTerm(variableName), new JavaObjectTerm(coll), inner});
	}

	/**
	 * Create a term for a forall connective
	 * Will look like:
	 * forall variable in coll do inner
	 * @param coll The Collection of Java Objects that will be forall-ed over
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	static public Term createForAllTerm(Oracle oracle, Collection coll, String variableName, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("forall", 5);
		Term list = makeList(coll);
		VariableTerm variable = oracle.getVariable(variableName);
		return new StructureTerm(name, new Term[]{variable, new JavaObjectTerm(variableName), new JavaObjectTerm(coll), list, inner});
	}
	
	/**
	 * Create a term for a forall connective
	 * Will look like:
	 * forall variable in coll do inner
	 * @param coll The Collection of Java Objects that will be forall-ed over
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	@Deprecated
	static public Term createForAllTerm(Collection coll, String variableName, VariableTerm variable, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("forall", 4);
		Term list = makeList(coll);
		return new StructureTerm(name, new Term[]{variable, new JavaObjectTerm(variableName), list, inner});
	}
	
	/**
	 * Create a term for a forall connective, but lazy-loading
	 * Will look like:
	 * forall variable in coll do inner
	 * @param coll An object that we will call back with to get the real list later
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	static public Term createExistsLazyTerm(Oracle oracle, Object coll, String variableName, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("existsLazy", 4);
		VariableTerm variable = oracle.getVariable(variableName);
		return new StructureTerm(name, new Term[]{variable, new JavaObjectTerm(variableName), new JavaObjectTerm(coll), inner});
	}
	
	/**
	 * Create a term for a forall connective
	 * Will look like:
	 * forall variable in coll do inner
	 * @param coll The Collection of Java Objects that will be forall-ed over
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	static public Term createExistsTerm(Oracle oracle, Collection coll, String variableName, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("exists", 5);
		Term list = makeList(coll);
		VariableTerm variable = oracle.getVariable(variableName);
		return new StructureTerm(name, new Term[]{variable, new JavaObjectTerm(variableName), new JavaObjectTerm(coll), list, inner});
	}
	/**
	 * Create a term for an exists connective
	 * Will look like:
	 * exists variable in coll do inner
	 * @param coll The Collection of Java Objects that will be looked over
	 * @param variable A variable that is used in the "inner" term.
	 * @param inner The term that will be instantiated for each object.
	 */
	@Deprecated
	static public Term createExistsTerm(Collection coll, VariableTerm variable, Term inner) {
		SymbolTerm name = SymbolTerm.makeSymbol("exists", 3);
		Term list = makeList(coll);
		return new StructureTerm(name, new Term[]{variable, list, inner});
	}
	
	/**
	 * Create a term for an implies connective
	 * Will look like:
	 * condition implies result
	 * @param cond The condition of the implies
	 * @param res The result of the implies
	 */
	static public Term createImpliesTerm(Term cond, Term res) {
		return new StructureTerm(SymbolTerm.makeSymbol("implies", 2), new Term[]{cond, res});
	}
	
	/**
	 * Create a term for an or connective
	 * Will look like:
	 * lhs or rhs
	 * @param lhs The left side of the or
	 * @param rhs The right side of the or
	 */
	static public Term createOrTerm(Term lhs, Term rhs) {
		return new StructureTerm(SymbolTerm.makeSymbol("or", 2), new Term[]{lhs, rhs});
	}
	
	/**
	 * Create a term for a not connective (for and and or)
	 * Will look like:
	 * not op
	 * @param op The operator
	 */
	static public Term createNotTerm(Term term) {
		return new StructureTerm(SymbolTerm.makeSymbol("not",1), new Term[]{term});
	}
	
	/** Given a collection, iterate and make it a ListTerm of JavaObjectTerms. If the collection
	 * is empty, it will just return a SymbolTerm for the nil list.
	 */
	static public Term makeList(Collection coll) {
		Term list = Prolog.Nil;
		Iterator itr = coll.iterator();
		Object obj;
		JavaObjectTerm jTerm;
		
		while (itr.hasNext()) {
			obj = itr.next();
			jTerm = new JavaObjectTerm(obj);
			list = new ListTerm(jTerm, list);
		}
		
		return list;
	}
}
